package com.alks.function.service.impl.finance.ryfinance;

import com.alks.common.aop.AutoPageAop;
import com.alks.common.data.dto.SysUserTokenDTO;
import com.alks.common.data.thread.UserIdThread;
import com.alks.common.utils.beanutils.ArrayUtils;
import com.alks.common.utils.beanutils.BeanUtil;
import com.alks.common.utils.excelUtils.ExcelUtils;
import com.alks.common.utils.stringUtils.ZStringUtils;
import com.alks.entity.data.entity.PcMtlPreOther;
import com.alks.entity.data.entity.PcMtlPreOther2;
import com.alks.entity.data.response.ResponseInfo;
import com.alks.entity.exception.ServiceErrorException;
import com.alks.function.data.dto.PcMtlPreCheckReq;
import com.alks.function.data.dto.finance.ryfinance.MtlPreOtherInDto;
import com.alks.function.data.request.finance.ryfinance.MtlPreOtherInRequest;
import com.alks.function.data.request.finance.ryfinance.MtlPreOtherUpRequest;
import com.alks.function.mapper.finance.ryfinance.PcMtlPreOtherMapper;
import com.alks.function.service.finance.ryfinance.IRecTbAccountService;
import com.baomidou.mybatisplus.extension.service.additional.update.impl.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.alks.entity.data.enums.ResultCodeEnum.DATA_IS_ERR;
import static com.alks.entity.data.enums.ResultCodeEnum.INPUT_ERROR;

/**
 * 财务部-荣阳财务-收货对账作业 实现类
 *
 * @author ms
 */
@Slf4j
@Service
public class RecTbAccountServiceImpl extends ServiceImpl<PcMtlPreOtherMapper, PcMtlPreOther> implements IRecTbAccountService {
    @Autowired
    private PcMtlPreOtherMapper otherMapper;

    @Override
    public void downloadMtlPreOther(HttpServletResponse response, MtlPreOtherInRequest request) throws IOException {
        Map<String, Object> map = new HashMap<>();
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        String date = format.format(new Date());
        String companyId = UserIdThread.get().getCompanyId();
        List<MtlPreOtherInDto> dtos = otherMapper.mtlPreOtherInQuery(request, companyId);
        map.put("date", date);
        ExcelUtils.creatByTemplate(response, "收货对账作业模板.xls", date, dtos, map);
    }

    @Override
    @AutoPageAop
    public ResponseInfo mtlPreOtherInQuery(MtlPreOtherInRequest request) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取基础数据*/
        List<MtlPreOtherInDto> dtos = otherMapper.mtlPreOtherInQuery(request, companyId);
        Page<MtlPreOtherInDto> page = new Page<>();
        if (!request.getPageSize().equals(-1)) {
            page = (Page<MtlPreOtherInDto>) dtos;
        }
        Map<String, Integer> sumMap = otherMapper.mtlPreOtherSumQuery(request, companyId);
        /*获取求和数据*/
        Map<String, Object> map = new HashMap<>(4);
        /*封装数据*/
        map.put("total", page.getTotal());
        map.put("dtos", dtos);
        map.put("amtSum", sumMap.get("amtSum"));
        map.put("sendQtySum", sumMap.get("amtSum"));
        return ResponseInfo.ok(map);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseInfo mtlPreOtherInUp(List<MtlPreOtherUpRequest> request) {
        SysUserTokenDTO userTokenDTO = UserIdThread.get();
        String companyId = userTokenDTO.getCompanyId();
        /*查找数据*/
        ResponseInfo<Object> responseInfo = upReqCheck(request, companyId, userTokenDTO);
        if (responseInfo != null) {
            return responseInfo;
        }
        String userName = userTokenDTO.getUserName();
        /*更新数据*/
        for (MtlPreOtherUpRequest r : request) {
            //定位数据
            LambdaUpdateChainWrapper<PcMtlPreOther> update = lambdaUpdate()
                    .eq(PcMtlPreOther::getCompanyId, companyId)
                    .eq(PcMtlPreOther::getPurType, r.getPurTypeOld())
                    .eq(StringUtil.isNotBlank(r.getPurNo()), PcMtlPreOther::getPurNo, r.getPurNo())
                    .isNull(StringUtil.isBlank(r.getPurNo()), PcMtlPreOther::getPurNo)
                    .eq(StringUtil.isNotBlank(r.getOrderId()), PcMtlPreOther::getOrderId, r.getOrderId())
                    .isNull(StringUtil.isBlank(r.getOrderId()), PcMtlPreOther::getOrderId)
                    .ge(r.getPurDate() != null, PcMtlPreOther::getPurDate, r.getPurDate())
                    .le(r.getPurDate() != null, PcMtlPreOther::getPurDate, r.getPurDate().plusDays(1))
                    .isNull(r.getPurDate() == null, PcMtlPreOther::getPurDate)
                    .eq(StringUtil.isNotBlank(r.getVendorName()), PcMtlPreOther::getVendorName, r.getVendorName())
                    .isNull(StringUtil.isBlank(r.getVendorName()), PcMtlPreOther::getVendorName)
                    .eq(StringUtil.isNotBlank(r.getMtlNo()), PcMtlPreOther::getMtlNo, r.getMtlNo())
                    .isNull(StringUtil.isBlank(r.getMtlNo()), PcMtlPreOther::getMtlNo);
            //表内基础数据更新
            if (r.getFinanceFlag() == null && BeanUtil.allIsEmpty(r.getVendorNo2New())) {
                update.set(PcMtlPreOther::getPurType, r.getPurType())
                        .set(PcMtlPreOther::getMtlName, r.getMtlName())
                        .set(PcMtlPreOther::getMtlSpec, r.getMtlSpec())
                        .set(PcMtlPreOther::getMtlUnit, r.getMtlUnit())
                        .set(PcMtlPreOther::getMtlColor, r.getMtlColor())
                        .set(PcMtlPreOther::getPurQty, r.getPurQty())
                        .set(PcMtlPreOther::getRemark, r.getRemark());
                //检核
            } else if (r.getFinanceFlag()!=null && r.getFinanceFlag().equals(1)) {
                update.set(PcMtlPreOther::getFinanceRecYymm, r.getFinanceRecYymmNew())
                        .set(PcMtlPreOther::getFinanceCfmFlag, "Y")
                        .set(PcMtlPreOther::getFinanceCfmUser, userName)
                        .set(PcMtlPreOther::getFinanceCfmDate, LocalDate.now());
                //取消检核
            } else if (r.getFinanceFlag()!=null && r.getFinanceFlag().equals(0)) {
                update.set(PcMtlPreOther::getFinanceRecYymm, null)
                        .set(PcMtlPreOther::getFinanceCfmFlag, null)
                        .set(PcMtlPreOther::getFinanceCfmUser, null)
                        .set(PcMtlPreOther::getFinanceCfmDate, null);
                //结算信息更新（上方导航栏更新）
            } else {
                update.set(PcMtlPreOther::getVendorNo2, r.getVendorNo2New())
                        .set(PcMtlPreOther::getVendorName2, r.getVendorName2New())
                        .set(PcMtlPreOther::getFinanceAccMemo, r.getFinanceAccMemoNew());
            }
            update.update();
        }
        return ResponseInfo.ok();
    }


    @Override
    public ResponseInfo mtlPreOtherInBack() {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取备注回显*/
        List<PcMtlPreOther> others = lambdaQuery()
                .select(PcMtlPreOther::getFinanceAccMemo)
                .eq(PcMtlPreOther::getCompanyId, companyId).list();
        List<String> memo = others.stream().map(PcMtlPreOther::getFinanceAccMemo).collect(Collectors.toList());
        List<String> purType = others.stream().map(PcMtlPreOther::getPurType).collect(Collectors.toList());
        /*封装返回*/
        Map<String, Object> map = new HashMap<>();
        map.put("memo", memo);
        map.put("purType", purType);
        return ResponseInfo.ok(map);
    }

    @Override
    @AutoPageAop
    public ResponseInfo pcMltPreCDList(PcMtlPreCheckReq req) {
        req.setCompanyId(UserIdThread.get().getCompanyId());
        Map<String, Object> map = new HashMap<>();
        List<PcMtlPreOther2> list = otherMapper.pcMltPreCDList(req);
        Page<PcMtlPreOther2> page = (Page<PcMtlPreOther2>) list;
        BigDecimal sendQtyTotal = otherMapper.getSendQtyTotal(req);
        BigDecimal priceTotal = otherMapper.getPriceTotal(req);
        map.put("list", list);
        map.put("sendQtyTotal", sendQtyTotal);
        map.put("amtTotal", priceTotal);
        map.put("total", page.getTotal());
        return ResponseInfo.ok(map);
    }

    @Override
    public ResponseInfo optionMtlPreCheck(List<PcMtlPreOther2> list, String choose) {
        otherMapper.optionMtlPreCheck(list, choose);
        return ResponseInfo.ok();
    }

    @Override
    @Transactional
    public ResponseInfo insertPriceWS(List<PcMtlPreOther2> list, BigDecimal discount, BigDecimal orgPrice, Date sendDate, String choose) {
        if (ZStringUtils.isEmpty(list)) return ResponseInfo.error("请勾选数据");
        switch (choose) {
            case "1":
                if (orgPrice==null||discount==null) return ResponseInfo.error("折扣跟原单价不能为空");
                BigDecimal price = orgPrice.multiply(discount).setScale(5, RoundingMode.HALF_UP);
                list.forEach(x -> {
                    x.setPrice(price);
                    x.setOrgPrice(orgPrice);
                    if (x.getPurQty()!=null) x.setAmt(price.multiply(x.getPurQty()).setScale(2, RoundingMode.HALF_UP));
                });
                otherMapper.setPriceInMtlPre(list);
                return ResponseInfo.ok();
            case "2":
                list.forEach(x -> x.setSendDate(sendDate));
                for (PcMtlPreOther2 other2 : list) {
                    BigDecimal amt = null;
                    if (other2.getPrice() != null) amt = other2.getPrice().multiply(other2.getPurQty()).setScale(5);
                    other2.setAmt(amt);
                    other2.setSendQty(other2.getPurQty());
                }
                otherMapper.setSendAmt(list);
                return ResponseInfo.ok();
            case "3":
                otherMapper.updateMtlPre(list);
                return ResponseInfo.ok();
        }
        return ResponseInfo.ok();
    }

    @Override
    public ResponseInfo getPurType() {
        List<String> list = otherMapper.getPurType();
        return ResponseInfo.ok(list);
    }


    /**
     * 对账检核
     *
     * @param request
     * @param companyId
     * @param userTokenDTO
     * @return
     */
    private @Nullable ResponseInfo<Object> upReqCheck(List<MtlPreOtherUpRequest> request, String companyId, SysUserTokenDTO userTokenDTO) {
        for (MtlPreOtherUpRequest r : request) {
            List<PcMtlPreOther> others = lambdaQuery()
                    .eq(PcMtlPreOther::getCompanyId, companyId)
                    .eq(PcMtlPreOther::getPurType, r.getPurTypeOld())
                    .eq(StringUtil.isNotBlank(r.getPurNo()), PcMtlPreOther::getPurNo, r.getPurNo())
                    .isNull(StringUtil.isBlank(r.getPurNo()), PcMtlPreOther::getPurNo)
                    .eq(StringUtil.isNotBlank(r.getOrderId()), PcMtlPreOther::getOrderId, r.getOrderId())
                    .isNull(StringUtil.isBlank(r.getOrderId()), PcMtlPreOther::getOrderId)
                    .ge(r.getPurDate() != null, PcMtlPreOther::getPurDate, r.getPurDate())
                    .le(r.getPurDate() != null, PcMtlPreOther::getPurDate, r.getPurDate() == null ? null : r.getPurDate().plusDays(1))
                    .isNull(r.getPurDate() == null, PcMtlPreOther::getPurDate)
                    .eq(StringUtil.isNotBlank(r.getVendorName()), PcMtlPreOther::getVendorName, r.getVendorName())
                    .isNull(StringUtil.isBlank(r.getVendorName()), PcMtlPreOther::getVendorName)
                    .eq(StringUtil.isNotBlank(r.getMtlNo()), PcMtlPreOther::getMtlNo, r.getMtlNo())
                    .isNull(StringUtil.isBlank(r.getMtlNo()), PcMtlPreOther::getMtlNo).list();
            /*没有找到数据处理*/
            if (ArrayUtils.isEmpyt(others)) {
                return ResponseInfo.error("修改失败，数据存在异常", DATA_IS_ERR.getCode());
                /*数据过多处理*/
            } else if (others.size() > 1) {
                String loginName = userTokenDTO.getLoginName();
                log.warn("\n异常接口：mtlPreOtherInUp\n异常原因：数据不唯一\n对应表：【PC_MTL_PER_OTHER】 用户：{}\n入参：{}\n", loginName, r);
            }
            /*无金额或者未检核不可对账*/
            if (r.getFinanceFlag()!=null && r.getFinanceFlag().equals(1)) {
                financeDateCheck(r.getFinanceRecYymmNew());
                if (others.size() > 1) {
                    return ResponseInfo.error("存在指令、材料、类型、订购单号都相同的数据，无法对账。指令为：" + r.getOrderId(), DATA_IS_ERR.getCode());
                }
                boolean amtNullFlag = others.stream()
                        .anyMatch(other -> other.getAmt() == null || other.getAmt().compareTo(BigDecimal.ZERO) == 0);
                if (amtNullFlag) {
                    log.error("\n错误接口：mtlPreOtherInUp\n错误原因：有数据金额为空，不能对账\n对应表：【PC_MTL_PER_OTHER】\n入参：{}\n", r);
                    return ResponseInfo.error("有数据金额为空，不能对账", INPUT_ERROR.getCode());
                }
                if (!"Y".equals(others.get(0).getCfmFlag())) {
                    log.error("\n错误接口：mtlPreOtherInUp\n错误原因：指令未检核，不能对账\n对应表：【PC_MTL_PER_OTHER】\n入参：{}\n", r);
                    return ResponseInfo.error("指令" + r.getOrderId() + "未检核，不能对账", INPUT_ERROR.getCode());
                }
            }
        }
        return null;
    }

    /**
     * 结算时间核验 过去一年到当前时间可以结算
     *
     * @param financeYymm
     * @return
     */
    public static @Nullable void financeDateCheck(String financeYymm) {
        if (StringUtils.isBlank(financeYymm)) {
            throw new ServiceErrorException("结算月份不能为空");
        }
        YearMonth financeRec;
        try {
            financeRec = YearMonth.parse(financeYymm, DateTimeFormatter.ofPattern("yyyyMM"));
        }catch (Exception e){
            throw new ServiceErrorException("结算月份格式有误");
        }
        //早于当前一年或者晚于当前时间不能检核
        if (financeRec.isBefore(YearMonth.now().minusYears(1)) || financeRec.isAfter(YearMonth.now())) {
            StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
            String callingMethodName = stackTraceElements[2].getMethodName();
            log.warn("异常接口：{}\n异常原因：早于当前一年或者晚于当前时间不能结算\n当前输入检核时间：{}\n", callingMethodName, financeYymm);
            throw new ServiceErrorException("结算月份有误");
        }
    }

}
